﻿using Framework.Cache;
using Framework.Utility.Extensions;
using Framework.Utility.Helper;
using Framework.Utility.ViewModels;
using Hangfire;
using Microsoft.Extensions.Logging;
using MonitorCode.DBCode;
using MonitorCode.Models;
using Support.Models;
using Support.Models.Dto;
using Support.Models.Entities;

namespace MonitorCode.Hangfire
{
    public class MonitorDataService : ServiceConfig, IMonitorDataService
    {
        private ILogger<MonitorDataService> _logger;

        public MonitorDataService(ILogger<MonitorDataService> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 模拟数据
        /// </summary>
        /// <returns></returns>
        public async Task<TData> GetMockData(int range = 7)
        {
            //var dateNow = DateTime.Now.Date;
            //var nameList = new List<string>();
            //var numList = new List<string>();
            //for (int i = range - 1; i >= 0; i--)
            //{
            //    var ddd = dateNow.AddDays(-i);
            //    var _list = JobSeed.JobMonitorDataRecords.Where(c => c.ProcessDate == ddd).ToList();

            //    nameList.Add(dateNow.AddDays(-i).Formatted("yyyy/MM/dd"));
            //    numList.Add(_list.Count().ToString());
            //}

            var list = new List<MonitorDataDto>();
            string _tt = "模拟项目_{0}";
            var sorce = JobSeed.JobMonitorDataRecords.OrderBy(c => c.ProcessDate).ToList(); 
        
            var values = new string[] {
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString(),
                new Random().Next(5, 50).ToString()
            }; 

            var monitorData = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            var count = monitorData.Where(c=>c.Status == JobFireStatus.Success).Count();
            for (int i = 0; i < count; i++)
            {
                MonitorDataDto item = new MonitorDataDto();
                item.title = string.Format(_tt, i + 1);
                item.key = Guid.NewGuid().ToString();
                item.names = sorce.Select(x => x.ProcessDate.Formatted("yyyy/MM/dd")).ToArray();
                item.nums = values;
                list.Add(item);
            }

            await Task.Delay(100);
            return new TData(ResultTag.success, "", list);
        }

        /// <summary>
        /// 获取监控面板
        /// </summary>
        /// <returns></returns>
        public async Task<TData> GetMonitorResult()
        {
            var monitorData = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            var monitorDataList = monitorData.Where(c => c.Status != JobFireStatus.No);
            var list = new List<MonitorDataDto>();
            await Parallel.ForEachAsync(monitorDataList, async (j, c) =>
            {
                MonitorDataDto dto = new MonitorDataDto();
                dto.title = j.ServerName;
                dto.key = j.JobId;
                dto.names = new string[] { };
                dto.nums = new string[] { };
                var dbInfo = JobSeed.JobDbLinkDatas.FirstOrDefault(c => c.Id == j.DbLinkId);
                if (dbInfo != null)
                {
                    MonitorDataConfig dbLink = new MonitorDataConfig()
                    {
                        DBType = dbInfo.DBType,
                        DBHostName = dbInfo.DBHostName,
                        DatabaseName = dbInfo.DatabaseName,
                        UserName = dbInfo.UserName,
                        Password = dbInfo.Password,
                        Port = dbInfo.Port,
                        Sql = "select Id,JobId,ProcessDate,ResultCount from Job_MonitorDataRecord",
                    };
                    var dbService = new DBFactory(dbLink).DBService();
                    var list = dbService.GetDataListBySql<Job_MonitorDataRecord>(dbLink.Sql);

                    dto.names = list.Select(x => x.ProcessDate.Formatted("yyyy/MM/dd")).Take(7).ToArray();
                    dto.nums = list.Select(x => x.ResultCount.ToString()).Take(7).ToArray();
                }
                list.Add(dto);
                await Task.Delay(1000);
            });
            return new TData(ResultTag.success, "", list);
        }

        /// <summary>
        /// 获取监控列表
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<TData<List<Job_MonitorData>>> GetJobFireMonitorDataListAsync(string keyword = "")
        {
            var data = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            var list = data.WhereIf(c => c.ServerName.Contains(keyword) || c.GroupName.Contains(keyword), !string.IsNullOrWhiteSpace(keyword)).ToList();
            var resultData = new TData<List<Job_MonitorData>>();
            resultData.data = list;
            resultData.total = list.Count;
            return resultData;
        }

        /// <summary>
        /// 添加数据监控
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<TData> AddJobMonitorFire(Job_MonitorData model)
        {
            var list = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            if (list.Any(c => c.ServerName == model.ServerName)) return new TData(ResultTag.fail);
            else
            {
                list.Add(model);
                await CacheFactory.Cache.SetAsync(CacheKay.JobMonitorData, list.ToJson());
            }
            return new TData(ResultTag.success);
        }

        /// <summary>
        /// 删除数据监控
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<TData> DeleteJobMonitorFire(string jobId)
        {
            var list = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            if (!list.Any(c => c.JobId == jobId)) return new TData(ResultTag.fail);
            else
            {
                list.RemoveAll(c => c.JobId == jobId);
                await CacheFactory.Cache.SetAsync(CacheKay.JobMonitorData, list.ToJson());
            }
            RecurringJob.RemoveIfExists(jobId);
            return new TData(ResultTag.success);
        }

        /// <summary>
        /// 开启数据监控
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<TData> StartJobMonitorFireAsync(string jobId)
        {
            try
            {
                var jobInfo = await GetJobMonitorFireAsync(jobId);
                if (jobInfo == null) return new TData(ResultTag.fail);
                var cronExpression = string.IsNullOrWhiteSpace(jobInfo.CronExpression) ? Cron.Minutely() : jobInfo.CronExpression;
                RecurringJob.AddOrUpdate(jobInfo.JobId, () => ExecuteJobMonitorData(jobInfo), cronExpression, TimeZoneInfo.Local, "default");

                SetMonitorDataJobFireStatus(jobInfo.JobId, JobFireStatus.Starting);

                return new TData(ResultTag.success);
            }
            catch (Exception ex)
            {
                return new TData(ResultTag.fail, ex.Message);
            }
        }

        /// <summary>
        /// 停止数据监控
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public TData StopJobMonitorFire(string jobId)
        {
            RecurringJob.RemoveIfExists(jobId);
            SetMonitorDataJobFireStatus(jobId, JobFireStatus.No);
            return new TData(ResultTag.success);
        }

        public async Task ExecuteJobMonitorData(Job_MonitorData model)
        {
            await ExecuteJobAndRetry(model);
        }

        private async Task<Job_MonitorData> GetJobMonitorFireAsync(string jobId)
        {
            var list = await CacheFactory.Cache.GetAsync<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            return list.FirstOrDefault(c => c.JobId == jobId);
        }

        /// <summary>
        /// 修改job 状态
        /// </summary>
        /// <param name="JobId"></param>
        /// <param name="status"></param>
        private void SetMonitorDataJobFireStatus(string? JobId, JobFireStatus status)
        {
            var list = CacheFactory.Cache.Get<List<Job_MonitorData>>(CacheKay.JobMonitorData) ?? new List<Job_MonitorData>();
            var info = list.FirstOrDefault(c => c.JobId == JobId);
            if (info == null) return;
            info.Status = status;
            info.CheckTime = (info.Status == JobFireStatus.Success || info.Status == JobFireStatus.Failure) ? DateTime.Now.ParseToString() : string.Empty;
            CacheFactory.Cache.Set(CacheKay.JobMonitorData, list);
        }

        /// <summary>
        /// 异常job处理
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private async Task ExceptionJobFire(Job_MonitorData jobInfo)
        {
            RecurringJob.RemoveIfExists(jobInfo.JobId);
            SetMonitorDataJobFireStatus(jobInfo.JobId, JobFireStatus.Exception);

            await SendJobResultEmail(jobInfo.JobId, jobInfo.ServerName, "数据异常");
        }

        /// <summary>
        /// 执行job
        /// 返回 200  返回监控成功，其他返回状态  重试3次 然后 延迟10分钟再执行一次ExecuteJob
        /// </summary>
        /// <param name="model"></param>
        /// <param name="retryTimes"></param>
        /// <returns></returns>
        private async Task ExecuteJobAndRetry(Job_MonitorData jobInfo, int retryTimes = 2)
        {
            if (retryTimes <= 0)
            {
                await ExceptionJobFire(jobInfo);
                return;
            }
            var dbInfo = JobSeed.JobDbLinkDatas.FirstOrDefault(c => c.Id == jobInfo.DbLinkId);

            MonitorDataConfig dbLink = new MonitorDataConfig()
            {
                DBType = dbInfo.DBType,
                DBHostName = dbInfo.DBHostName,
                DatabaseName = dbInfo.DatabaseName,
                UserName = dbInfo.UserName,
                Password = dbInfo.Password,
                Port = dbInfo.Port,
                Sql = jobInfo.SqlText,
            };

            var dbService = new DBFactory(dbLink).DBService();
            var model = dbService.GetCountBySql(dbLink.Sql);

            //string sql = $"insert into Job_MonitorDataRecord(JobId, ProcessDate, ResultCount, CreateTime) values('{jobInfo.JobId}', '{DateTime.Now.Date}', {model.data}, '{DateTime.Now}')";
            //var result = dbService.ExecuteNonQuery(sql);

            var result = model;

            if (result.code == ResultTag.success)
            {
                SetMonitorDataJobFireStatus(jobInfo.JobId, JobFireStatus.Success);
            }
            else
            {
                retryTimes--;
                LogHelper.Error($"ExecuteJob：{jobInfo.JobId}，剩余重试次数: {retryTimes}, Exception detail: {result?.message}");
                await Task.Delay(30000);
                await ExecuteJobAndRetry(jobInfo, retryTimes);
            }
        }
    }
}